package net.linku.service.impl;

import lombok.extern.slf4j.Slf4j;
import net.linku.constant.LoginConstant;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

@Service
@Slf4j
public class UserServiceDetailsServiceImpl implements UserDetailsService {

    @Resource
    @Qualifier("adminJdbcTemplate")
    private JdbcTemplate adminJdbcTemplate;

    @Resource
    @Qualifier("portalJdbcTemplate")
    private JdbcTemplate portalJdbcTemplate;

    /**
     * 根据用户名加载用户详细信息
     *
     * @param username 用户名
     * @return UserDetails 用户详细信息
     * @throws UsernameNotFoundException 用户名未找到异常
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        // 获取请求的属性
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        // 获取登录类型
        String loginType = requestAttributes.getRequest().getParameter("login_type");
        // 获取租户ID
        Long tenantId = getTenantId(requestAttributes);

        // 检查登录类型是否为空
        if (StringUtils.isEmpty(loginType)) {
            throw new AuthenticationServiceException("登录类型不能为空！");
        }

        // 检查是否为刷新类型的请求
        String grantType = requestAttributes.getRequest().getParameter("grant_type");
        if (LoginConstant.REFRESH_TYPE.equals(grantType.toUpperCase())) {
            // 调整用户名以进行刷新
            username = adjustUsername(username, loginType);
        }

        // 获取对应登录类型的JdbcTemplate
        JdbcTemplate jdbcTemplate = getJdbcTemplateByLoginType(loginType);
        // 获取查询SQL
        String sql = getQuerySqlByLoginType(loginType);

        // 打印调试信息
        log.info("Debug SQL: {}", formatSql(sql, username, tenantId));

        // 执行查询，获取用户详细信息列表
        List<UserDetails> userDetailsList = jdbcTemplate.query(
                sql,
                getQueryParameters(username, loginType, tenantId),
                getUserRowMapper(loginType, tenantId)
        );

        // 如果查询结果为空，抛出异常
        if (userDetailsList.isEmpty()) {
            log.error("查询无结果: 用户名 = {}, 租户ID = {}", username, tenantId);
            throw new UsernameNotFoundException("用户名 " + username + " 不存在");
        } else {
            log.info("查询结果: {} 条记录，用户名 = {}, 租户ID = {}", userDetailsList.size(), username, tenantId);
        }

        // 返回查询到的用户详细信息
        return userDetailsList.get(0);
    }

    /**
     * 从请求中获取租户ID
     *
     * @param requestAttributes 请求属性
     * @return Long 租户ID
     */
    private Long getTenantId(ServletRequestAttributes requestAttributes) {
        // 从请求头中获取租户ID
        String tenantIdStr = requestAttributes.getRequest().getHeader("X-Tenant-ID");
        // 检查租户ID是否为空
        if (StringUtils.isEmpty(tenantIdStr)) {
            throw new AuthenticationServiceException("租户ID不能为空！");
        }
        // 返回租户ID
        return Long.valueOf(tenantIdStr);
    }

    /**
     * 调整用户名，用于刷新令牌时的查询
     *
     * @param username  用户名
     * @param loginType 登录类型
     * @return String 调整后的用户名
     */
    private String adjustUsername(String username, String loginType) {
        // 获取调整用户名的SQL
        String querySql = getAdjustUsername(loginType);
        // 获取对应的JdbcTemplate
        JdbcTemplate jdbcTemplate = getJdbcTemplateByLoginType(loginType);
        // 执行查询，返回调整后的用户名
        return jdbcTemplate.queryForObject(querySql, String.class, username);
    }

    /**
     * 根据登录类型获取调整用户名的SQL
     *
     * @param loginType 登录类型
     * @return String 查询SQL
     */
    private String getAdjustUsername(String loginType) {
        switch (loginType) {
            case LoginConstant.ADMIN_TYPE:
                return LoginConstant.QUERY_ADMIN_USER_WITH_ID;
            case LoginConstant.MEMBER_TYPE:
                return LoginConstant.QUERY_MEMBER_SQL;
            case LoginConstant.PORTAL_TYPE:
                return LoginConstant.QUERY_PORTAL_USER_WITH_ID;
            default:
                throw new AuthenticationServiceException("暂不支持的登录方式：" + loginType);
        }
    }

    /**
     * 根据登录类型获取查询SQL
     *
     * @param loginType 登录类型
     * @return String 查询SQL
     */
    private String getQuerySqlByLoginType(String loginType) {
        switch (loginType) {
            case LoginConstant.ADMIN_TYPE:
                return LoginConstant.QUERY_ADMIN_SQL;
            case LoginConstant.MEMBER_TYPE:
                return LoginConstant.QUERY_MEMBER_SQL;
            case LoginConstant.PORTAL_TYPE:
                return LoginConstant.QUERY_PORTAL_SQL;
            default:
                throw new AuthenticationServiceException("暂不支持的登录方式：" + loginType);
        }
    }

    /**
     * 获取对应登录类型的JdbcTemplate
     *
     * @param loginType 登录类型
     * @return JdbcTemplate JdbcTemplate实例
     */
    private JdbcTemplate getJdbcTemplateByLoginType(String loginType) {
        if (LoginConstant.PORTAL_TYPE.equals(loginType)) {
            return portalJdbcTemplate; // 返回门户JdbcTemplate
        } else if (LoginConstant.ADMIN_TYPE.equals(loginType) || LoginConstant.MEMBER_TYPE.equals(loginType)) {
            return adminJdbcTemplate; // 返回管理员JdbcTemplate
        } else {
            throw new AuthenticationServiceException("暂不支持的登录方式：" + loginType);
        }
    }

    /**
     * 获取查询参数
     *
     * @param username  用户名
     * @param loginType 登录类型
     * @param tenantId  租户ID
     * @return Object[] 查询参数数组
     */
    private Object[] getQueryParameters(String username, String loginType, Long tenantId) {
        if (LoginConstant.ADMIN_TYPE.equals(loginType)) {
            return new Object[]{username, tenantId}; // 管理员类型，添加用户名和租户ID
        } else {
            return new Object[]{username, username, tenantId}; // 其他类型，添加用户名和租户ID
        }
    }

    /**
     * 获取用户行映射器
     *
     * @param loginType 登录类型
     * @param tenantId  租户ID
     * @return RowMapper<UserDetails> 行映射器
     */
    private RowMapper<UserDetails> getUserRowMapper(String loginType, Long tenantId) {
        return (rs, rowNum) -> {
            long id = rs.getLong("id"); // 获取用户ID
            String password = rs.getString("password"); // 获取用户密码
            boolean enabled = rs.getInt("enabled_flag") == 1; // 检查用户是否启用
            // 获取用户权限
            Set<SimpleGrantedAuthority> authorities = getAuthorities(id, loginType, tenantId);

            // 打印用户信息
            log.info("用户ID: {}, 用户名: {}, 是否启用: {}, 权限: {}",
                    id, rs.getString("account_code"), enabled,
                    authorities.stream().map(SimpleGrantedAuthority::getAuthority).collect(Collectors.joining(", ")));

            // 创建并返回User对象
            return new User(String.valueOf(id), password, enabled, true, true, true, authorities);
        };
    }

    /**
     * 获取用户权限
     *
     * @param userId    用户ID
     * @param loginType 登录类型
     * @param tenantId  租户ID
     * @return Set<SimpleGrantedAuthority> 用户权限集合
     */
    private Set<SimpleGrantedAuthority> getAuthorities(long userId, String loginType, Long tenantId) {
        if (LoginConstant.PORTAL_TYPE.equals(loginType)) {
            Set<SimpleGrantedAuthority> authorities = new HashSet<>();
            authorities.add(new SimpleGrantedAuthority("ROLE_USER")); // 添加用户角色
            log.info("用户ID: {}, 登录类型: {}, 权限: ROLE_USER", userId, loginType);
            return authorities;
        } else {
            // 查询用户角色代码和权限
            String roleCode = adminJdbcTemplate.queryForObject(LoginConstant.QUERY_ROLE_CODE_SQL, String.class, userId, tenantId);
            List<String> permissions = adminJdbcTemplate.queryForList(LoginConstant.QUERY_PERMISSION_SQL, String.class, userId, tenantId);

            // 转换权限为SimpleGrantedAuthority集合
            Set<SimpleGrantedAuthority> authorities = permissions.stream()
                    .map(SimpleGrantedAuthority::new)
                    .collect(Collectors.toSet());

            log.info("用户ID: {}, 登录类型: {}, 角色代码: {}, 用户权限: {}", userId, loginType, roleCode, permissions);

            return authorities;
        }
    }

    /**
     * 格式化SQL语句，替换占位符为实际参数
     *
     * @param sql    原始SQL语句
     * @param params 参数列表
     * @return String 格式化后的SQL语句
     */
    private String formatSql(String sql, Object... params) {
        StringBuilder formattedSql = new StringBuilder();
        String[] parts = sql.split("\\?"); // 根据问号分割SQL

        for (int i = 0; i < parts.length; i++) {
            formattedSql.append(parts[i]); // 添加SQL片段
            if (i < parts.length) { // 确保不在最后部分添加参数
                if (i < params.length) { // 确保不会越界
                    if (params[i] instanceof String) {
                        formattedSql.append("'").append(params[i]).append("'"); // 添加字符串参数
                    } else {
                        formattedSql.append(params[i]); // 添加其他类型参数
                    }
                } else {
                    formattedSql.append("NULL"); // 如果没有提供参数，则添加 NULL
                }
            }
        }

        return formattedSql.toString(); // 返回格式化后的SQL
    }
}
